From 6afe63320b03742cfa3ab9943347b4efecc3e2f6 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Mon, 26 May 2014 13:54:24 -0700 Subject: [PATCH] Add support for updating git repos --- src/cargo/sources/git.rs | 122 ++++++++++++++++++++++++++++++--------- 1 file changed, 94 insertions(+), 28 deletions(-) diff --git a/src/cargo/sources/git.rs b/src/cargo/sources/git.rs index 03af0356a..6cdee585a 100644 --- a/src/cargo/sources/git.rs +++ b/src/cargo/sources/git.rs @@ -2,6 +2,7 @@ use url::Url; use util::{CargoResult,ProcessBuilder,io_error,human_error,process}; +use std::fmt::Show; use std::str; use std::io::{UserDir,AllPermissions}; use std::io::fs::{mkdir_recursive,rmdir_recursive,chmod}; @@ -67,65 +68,130 @@ impl GitCommand { GitCommand { config: GitConfig { path: path, uri: uri, reference: reference } } } - pub fn checkout(&self) -> CargoResult { - let config = &self.config; + pub fn get_cwd<'a>(&'a self) -> &'a Path { + &self.config.path + } - if config.path.exists() { - git!(*config, "fetch --force --quiet --tags {} refs/heads/*:refs/heads/*", config.uri); + pub fn checkout(&self) -> CargoResult { + if self.config.path.exists() { + // TODO: If the revision we have is a rev, avoid unnecessarily fetching if we have the rev already + try!(self.fetch()); } else { - let dirname = Path::new(config.path.dirname()); - let mut checkout_config = self.config.clone(); - checkout_config.path = dirname; + try!(self.clone()); + } + + Ok(GitRepo { config: self.config.clone(), revision: try!(rev_for(&self.config)) }) + } + + fn fetch(&self) -> CargoResult<()> { + Ok(git!(self.config.path, "fetch --force --quiet --tags {} refs/heads/*:refs/heads/*", self.config.uri)) + } + + fn clone(&self) -> CargoResult<()> { + let dirname = Path::new(self.config.path.dirname()); - try!(mkdir_recursive(&checkout_config.path, UserDir).map_err(|err| - human_error(format!("Couldn't recursively create `{}`", checkout_config.path.display()), format!("path={}", checkout_config.path.display()), io_error(err)))); + try!(mkdir_recursive(&self.config.path, UserDir).map_err(|err| + human_error(format!("Couldn't recursively create `{}`", dirname.display()), format!("path={}", dirname.display()), io_error(err)))); - git!(checkout_config, "clone {} {} --bare --no-hardlinks --quiet", config.uri, config.path.display()); + Ok(git!(dirname, "clone {} {} --bare --no-hardlinks --quiet", self.config.uri, self.config.path.display())) + } +} + +struct GitCheckout<'a> { + location: Path, + repo: &'a GitRepo +} + +impl<'a> GitCheckout<'a> { + fn clone<'a>(into: Path, repo: &'a GitRepo) -> CargoResult> { + let checkout = GitCheckout { location: into, repo: repo }; + + // If the git checkout already exists, we don't need to clone it again + if !checkout.location.join(".git").exists() { + try!(checkout.clone_repo()); } - Ok(GitRepo { config: config.clone(), revision: try!(rev_for(config)) }) + Ok(checkout) + } + + fn get_source<'a>(&'a self) -> &'a Path { + self.repo.get_path() + } + + fn clone_repo(&self) -> CargoResult<()> { + try!(mkdir_recursive(&Path::new(self.location.dirname()), UserDir).map_err(io_error)); + try!(rmdir_recursive(&self.location).map_err(io_error)); + + git!(self.location, "clone --no-checkout --quiet {} {}", self.get_source().display(), self.location.display()); + try!(chmod(&self.location, AllPermissions).map_err(io_error)); + + Ok(()) + } + + fn fetch(&self) -> CargoResult<()> { + Ok(git!(self.location, "fetch --force --quiet --tags {}", self.get_source().display())) + } + + fn reset(&self, revision: T) -> CargoResult<()> { + Ok(git!(self.location, "reset --hard {}", revision)) + } + + fn update_submodules(&self) -> CargoResult<()> { + Ok(git!(self.location, "submodule update --init --recursive")) } } impl GitRepo { + fn get_path<'a>(&'a self) -> &'a Path { + &self.config.path + } + #[allow(unused_variable)] - fn copy_to(destination: &Path) -> CargoResult<()> { - Ok(()) + fn copy_to<'a>(&'a self, dest: Path) -> CargoResult> { + let checkout = try!(GitCheckout::clone(dest, self)); + + try!(checkout.fetch()); + try!(checkout.reset(self.revision.as_slice())); + try!(checkout.update_submodules()); + + Ok(checkout) } fn clone_to(&self, destination: &Path) -> CargoResult<()> { try!(mkdir_recursive(&Path::new(destination.dirname()), UserDir).map_err(io_error)); try!(rmdir_recursive(destination).map_err(io_error)); - git!(self.config, "clone --no-checkout --quiet {} {}", self.config.path.display(), destination.display()); + git!(self.config.path, "clone --no-checkout --quiet {} {}", self.config.path.display(), destination.display()); try!(chmod(destination, AllPermissions).map_err(io_error)); - let mut dest_config = self.config.clone(); - dest_config.path = destination.clone(); - - git!(dest_config, "fetch --force --quiet --tags {}", self.config.path.display()); - git!(dest_config, "reset --hard {}", self.revision); - git!(dest_config, "submodule update --init --recursive"); + git!(*destination, "fetch --force --quiet --tags {}", self.config.path.display()); + git!(*destination, "reset --hard {}", self.revision); + git!(*destination, "submodule update --init --recursive"); Ok(()) } } fn rev_for(config: &GitConfig) -> CargoResult { - Ok(git_output!(*config, "rev-parse {}", config.reference)) + Ok(git_output!(config.path, "rev-parse {}", config.reference)) +} + +#[allow(dead_code)] +fn has_rev(path: &Path, rev: T) -> bool { + git_output(path, format!("cat-file -e {}", rev)).is_ok() } -fn git(config: &GitConfig, str: &str) -> ProcessBuilder { - println!("Executing git {} @ {}", str, config.path.display()); - process("git").args(str.split(' ').collect::>().as_slice()).cwd(config.path.clone()) +fn git(path: &Path, str: &str) -> ProcessBuilder { + println!("Executing git {} @ {}", str, path.display()); + process("git").args(str.split(' ').collect::>().as_slice()).cwd(path.clone()) } -fn git_inherit(config: &GitConfig, str: String) -> CargoResult<()> { - git(config, str.as_slice()).exec().map_err(|err| +fn git_inherit(path: &Path, str: String) -> CargoResult<()> { + git(path, str.as_slice()).exec().map_err(|err| human_error(format!("Couldn't execute `git {}`: {}", str, err), None::<&str>, err)) } -fn git_output(config: &GitConfig, str: String) -> CargoResult { - let output = try!(git(config, str.as_slice()).exec_with_output().map_err(|err| +fn git_output(path: &Path, str: String) -> CargoResult { + let output = try!(git(path, str.as_slice()).exec_with_output().map_err(|err| human_error(format!("Couldn't execute `git {}`", str), None::<&str>, err))); Ok(to_str(output.output.as_slice())) -- 2.30.2